﻿
//#define EnableUserActivityHookInDebug2

using SharpDepend.WPF.Controls;
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Input;

namespace SharpDepend.WPF
{
    /// <summary>
    /// Reference: http://blogs.msdn.com/b/toub/archive/2006/05/03/589423.aspx
    /// Reference: http://stackoverflow.com/questions/17090325/show-hiding-the-windows-8-on-screen-keyboard-from-wpf
    /// </summary>
    public class KeyboardManager : IKeyboardManager
    {
#if EnableUserActivityHookInDebug2

#region Keyboard hook
        private const int WH_KEYBOARD_LL = 13;
        private const int WM_KEYDOWN = 0x0100;
        private IntPtr _hookID = IntPtr.Zero;
        private readonly LowLevelKeyboardProc _proc;

        private IntPtr SetHook(LowLevelKeyboardProc proc)
        {
            using (Process curProcess = Process.GetCurrentProcess())
            using (ProcessModule curModule = curProcess.MainModule)
            {
                return SetWindowsHookEx(WH_KEYBOARD_LL, proc,
                    GetModuleHandle(curModule.ModuleName), 0);
            }
        }

        private delegate IntPtr LowLevelKeyboardProc(int nCode, IntPtr wParam, IntPtr lParam);

        private IntPtr HookCallback(int nCode, IntPtr wParam, IntPtr lParam)
        {
            try
            {
                if (nCode >= 0 && wParam == (IntPtr)WM_KEYDOWN)
                {
                    int vkCode = Marshal.ReadInt32(lParam);

                    var call = OnKeyPressed;
                    if (call != null)
                    {
                        // Correct some keys.
                        switch (vkCode)
                        {
                            case 8: vkCode = (int)Key.Back; break;
                            case 13: vkCode = (int)Key.Return; break;
                            //case 188: vkCode = (int)Key.OemComma; break;
                            //case 190: vkCode = (int)Key.OemPeriod; break;
                            case 188: vkCode = ','; break;
                            case 190: vkCode = '.'; break;
                            case 187: vkCode = '+'; break;
                            case 189: vkCode = '-'; break;
                            case 53: vkCode = '%'; break;

                            case 37: vkCode = (int)Key.Left; break;
                            case 39: vkCode = (int)Key.Right; break;

                            case 160:
                                goto breakIf;
                        }

                        Key modifier = Key.None;

                        if (Keyboard.IsKeyDown(System.Windows.Input.Key.LeftShift))
                        {
                            modifier = Key.LeftShift;
                        }
                        else if (Keyboard.IsKeyDown(System.Windows.Input.Key.RightShift))
                        {
                            modifier = Key.RightShift;
                        }
                        else if (Keyboard.IsKeyDown(System.Windows.Input.Key.LeftCtrl))
                        {
                            modifier = Key.LeftCtrl;
                        }
                        else if (Keyboard.IsKeyDown(System.Windows.Input.Key.RightCtrl))
                        {
                            modifier = Key.RightCtrl;
                        }
                        else if (Keyboard.IsKeyDown(System.Windows.Input.Key.LeftAlt))
                        {
                            modifier = Key.LeftAlt;
                        }
                        else if (Keyboard.IsKeyDown(System.Windows.Input.Key.RightAlt))
                        {
                            modifier = Key.RightAlt;
                        }

                        if (modifier == Key.LeftShift || modifier == Key.RightShift)
                        {
                            switch (vkCode)
                            {
                                case 55: vkCode = '/'; break;
                                case 56: vkCode = '('; break;
                                case 57: vkCode = ')'; break;
                                case 186: vkCode = '^'; break;
                                case 191: vkCode = '*'; break;
                                default:
                                    break;
                            }
                        }
                        else
                        {
                            if (vkCode >= 65 && vkCode <= 90)
                            {
                                vkCode += 32;
                            }
                        }

                        call((Key)vkCode, (char)vkCode, modifier);
                    }
                }

                breakIf:;
            }
            catch (Exception)
            {
            }

            return CallNextHookEx(_hookID, nCode, wParam, lParam);
        }

        [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        private static extern IntPtr SetWindowsHookEx(int idHook, LowLevelKeyboardProc lpfn, IntPtr hMod, uint dwThreadId);

        [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        private static extern bool UnhookWindowsHookEx(IntPtr hhk);

        [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        private static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode, IntPtr wParam, IntPtr lParam);

        [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        private static extern IntPtr GetModuleHandle(string lpModuleName);

        #endregion

        #region Windows 8 show/hide virtual keyboard.

        //protected override void OnStartup(StartupEventArgs eventArgs)
        //{
        //    EventManager.RegisterClassHandler(typeof(TextBox), UIElement.GotFocusEvent,
        //                            new RoutedEventHandler(GotFocus_Event), true);
        //    EventManager.RegisterClassHandler(typeof(TextBox), UIElement.LostFocusEvent,
        //                            new RoutedEventHandler(LostFocus_Event), true);

        //    MainApplication.Show();
        //}

        //private static void GotFocus_Event(object sender, RoutedEventArgs e)
        //{
        //    // TestKeyboard();
        //    OpenWindows8TouchKeyboard(sender, e);
        //}
        ////http://www.c-sharpcorner.com/UploadFile/29d7e0/get-the-key-board-details-of-your-system-in-windows-form/
        //private static bool IsSurfaceKeyboardAttached()
        //{
        //    SelectQuery Sq = new SelectQuery("Win32_Keyboard");
        //    ManagementObjectSearcher objOSDetails = new ManagementObjectSearcher(Sq);
        //    ManagementObjectCollection osDetailsCollection = objOSDetails.Get();
        //    //Windows 8 tablet are returnign 2 device when keyboard is connecto
        //    //My application won't be used for Desktop so this condition is fine
        //    //But u might want to see if keyboard is usb and == 1 so you are 
        //    //returning true or not on tablet.  
        //    return osDetailsCollection.Count <= 1 ? true : false;
        //}

        //private static void OpenWindows8TouchKeyboard(object sender, RoutedEventArgs e)
        //{
        //    var textBox = e.OriginalSource as TextBox;
        //    if (textBox != null && IsSurfaceKeyboardAttached())
        //    {
        //        var path = @"C:\Program Files\Common Files\Microsoft Shared\ink\TabTip.exe";
        //        if (!File.Exists(path))
        //        {
        //            // older windows versions
        //            path = Environment.GetFolderPath(Environment.SpecialFolder.System) + @"\osk.exe";
        //        }
        //        Process.Start(path);
        //        textBox.BringIntoView();//SetFocus so u dont lose focused area
        //    }
        //}
        //[DllImport("user32.dll")]
        //public static extern int FindWindow(string lpClassName, string lpWindowName);

        //[DllImport("user32.dll")]
        //public static extern int SendMessage(int hWnd, uint Msg, int wParam, int lParam);

        //public const int WM_SYSCOMMAND = 0x0112;
        //public const int SC_CLOSE = 0xF060;
        //public const int SC_MINIMIZE = 0xF020;

        //private void CloseOnscreenKeyboard()
        //{
        //    // retrieve the handler of the window  
        //    int iHandle = FindWindow("IPTIP_Main_Window", "");
        //    if (iHandle > 0)
        //    {
        //        // close the window using API        
        //        SendMessage(iHandle, WM_SYSCOMMAND, SC_CLOSE, 0);
        //    }
        //}

        //private void LostFocus_Event(object sender, EventArgs e)
        //{
        //    // It's time to close the onscreen keyboard.
        //    CloseOnscreenKeyboard();
        //}

        #endregion

#endif

        public static Window MainWindow { get; set; }

        public event KeyPressedHandler OnKeyPressed;

#if EnableUserActivityHookInDebug2
        public KeyboardManager()
        {
            _proc = HookCallback;
            _hookID = SetHook(_proc);
        }

        ~KeyboardManager()
        {
            UnhookWindowsHookEx(_hookID);
        }
#endif

        public KeybordSupport KeybordSupport
        {
            get
            {
                return KeybordSupport.RealKeyboard;
            }
        }

        public bool IsKeyDown(Key key)
        {
            System.Windows.Input.Key windowKey = (System.Windows.Input.Key)key;
            return Keyboard.IsKeyDown(windowKey);
        }
        
        public virtual void ShowVirtualKeyboard(string titleHint, string descriptionHint, string defaultText, KeyboardTextChanged onTextChanged)
        {
            TextInput inputContent = new TextInput();
            inputContent.Label = descriptionHint ?? string.Empty;
            inputContent.Text = defaultText ?? string.Empty;

            Window window = new Window();
            window.Owner = MainWindow;
            window.Title = titleHint ?? string.Empty;
            window.Content = inputContent;
            window.ShowDialog();

            onTextChanged(inputContent.Text);
        }
    }
}
